#include "Model.h"
#include <string>
#include <sstream>
#include <fstream>
#include <memory.h>

#include "Utils.h"

using namespace std;

CRenderer* CModel::m_pRenderer = NULL;

CModel::CModel(void)
{
	m_cgProgVertex = 0;
	m_cgProgFragment = 0;
	m_bMultiTextured = 0;
}

CModel::CModel(bool bMultiTextured)
{
	m_cgProgVertex = 0;
	m_cgProgFragment = 0;
	m_bMultiTextured = bMultiTextured;
}

CModel::~CModel(void)
{

}

int CModel::ProcessLine(FILE* fileModel)
{
	char szLine[1000];
	if(fscanf_s(fileModel, "%[^\n]", szLine, 1000)!=EOF) //read single line
	{
		if(fscanf_s(fileModel, "\n", 2) == EOF) return -1;	//read line end, and sense EOF
		char szToken[1000];
		sscanf_s(szLine, "%s", szToken, 1000);	//read single token
		if(strcmp(szToken, "v")==0)	//vertex position
		{
			float3 f3Pos;
			sscanf_s(szLine+2, "%f %f %f", &f3Pos.x, &f3Pos.y, &f3Pos.z, 1000);
			m_vPos.push_back(f3Pos);
		}
		else if(strcmp(szToken, "vt")==0)	//vertex texture coords
		{
			float2 f2TexCoord;
			sscanf_s(szLine+3, "%f %f", &f2TexCoord.u, &f2TexCoord.v, 1000);

			//invert coordinates to fit fake bitmap loading(?)
		//	f2TexCoord.u = 1-f2TexCoord.u;
			f2TexCoord.v = 1-f2TexCoord.v;
			m_vTexCoord.push_back(f2TexCoord);
		}
		else if(strcmp(szToken, "vn")==0)	//vertex normal
		{
			float3 f3Normal;
			sscanf_s(szLine+3, "%f %f %f", &f3Normal.x, &f3Normal.y, &f3Normal.z, 1000);
			m_vNormal.push_back(f3Normal);
		}
		else if(strcmp(szToken, "f")==0)	//face description
		{
			//split face description to three vertex descriptions
			char szVertDesc[3][100];	//3 descriptions, every 100 chars length
			sscanf_s(szLine+2, "%s", szVertDesc[0], 100);
			sscanf_s(szLine+3+strlen(szVertDesc[0]), "%s", szVertDesc[1], 100);
			sscanf_s(szLine+4+strlen(szVertDesc[0])+strlen(szVertDesc[1]), "%s", szVertDesc[2], 100);

			for(int i=0; i<3; i++)	//read 3 vertices of triangle
			{
				
				int iPosInd, iTexInd, iNormInd;		//indices of parameters in vector
				sscanf_s(szVertDesc[i], "%d/%d/%d", &iPosInd, &iTexInd, &iNormInd, 1000);
				CRenderer::SVert pVert(m_vPos[iPosInd-1], m_vNormal[iNormInd-1], m_vTexCoord[iTexInd-1]);
				m_vVertices.push_back(pVert);
			}
		}
	}
	else
	{
		return -1;	//return end of file
	}
	return 0; //return OK
}

int CModel::LoadModel(const char* szFileName)
{
	FILE* fileModel;
	fopen_s(&fileModel, szFileName, "r");
	while( ProcessLine(fileModel) != -1)	//process whole file
	{
	}
	fclose(fileModel);

	m_pVertBuffer = new CRenderer::SVert[m_vVertices.size()];
	m_pIndBuffer = new unsigned short[m_vVertices.size()];

	for(unsigned short i=0; i<m_vVertices.size(); i++)
	{
		//create simple index
		m_pIndBuffer[i] = i;
		m_pVertBuffer[i] = m_vVertices[i];
	}

	//create and bind VBO
	glGenBuffersARB(1, &m_uiVBOVertices);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_uiVBOVertices);
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(CRenderer::SVert)*m_vVertices.size(), m_pVertBuffer, GL_STATIC_DRAW_ARB);

	//create and bind IBO
	glGenBuffersARB(1, &m_uiVBOIndices);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_uiVBOIndices);
	glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(unsigned short)*m_vVertices.size(), m_pIndBuffer, GL_STATIC_DRAW_ARB);

	m_iVerticesCount = (int)m_vVertices.size();

	//free buffers:
	delete[] m_pVertBuffer;
	delete[] m_pIndBuffer;
	m_vVertices.empty();

	return 0;
}

int CModel::CreateTexBox(float fXS, float fYS, float fZS, float fXTS, float fYTS, float fZTS)
{
	float fSize = 1.0;
	//create data buffers:

	CRenderer::SVert vertBuf[4*6] = //6 sides, 4 vertices per side
	{
		//back side
		CRenderer::SVert( float3( -fXS/2, -fYS/2, -fZS/2), float3(  0.0f,  0.0f, -1.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3( -fXS/2,  fYS/2, -fZS/2), float3(  0.0f,  0.0f, -1.0f), float2( 0.0f, fYTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2, -fZS/2), float3(  0.0f,  0.0f, -1.0f), float2( fXTS, fYTS)),
		CRenderer::SVert( float3(  fXS/2, -fYS/2, -fZS/2), float3(  0.0f,  0.0f, -1.0f), float2( fXTS, 0.0f)),

		//front side
		CRenderer::SVert( float3( -fXS/2, -fYS/2,  fZS/2), float3(  0.0f,  0.0f,  1.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3( -fXS/2,  fYS/2,  fZS/2), float3(  0.0f,  0.0f,  1.0f), float2( 0.0f, fYTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2,  fZS/2), float3(  0.0f,  0.0f,  1.0f), float2( fXTS, fYTS)),
		CRenderer::SVert( float3(  fXS/2, -fYS/2,  fZS/2), float3(  0.0f,  0.0f,  1.0f), float2( fXTS, 0.0f)),

		//bottom side
		CRenderer::SVert( float3( -fXS/2, -fYS/2, -fZS/2), float3(  0.0f, -1.0f,  0.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3( -fXS/2, -fYS/2,  fZS/2), float3(  0.0f, -1.0f,  0.0f), float2( 0.0f, fZTS)),
		CRenderer::SVert( float3(  fXS/2, -fYS/2,  fZS/2), float3(  0.0f, -1.0f,  0.0f), float2( fXTS, fZTS)),
		CRenderer::SVert( float3(  fXS/2, -fYS/2, -fZS/2), float3(  0.0f, -1.0f,  0.0f), float2( fXTS, 0.0f)),

		//top side
		CRenderer::SVert( float3( -fXS/2,  fYS/2, -fZS/2), float3(  0.0f,  1.0f,  0.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3( -fXS/2,  fYS/2,  fZS/2), float3(  0.0f,  1.0f,  0.0f), float2( 0.0f, fZTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2,  fZS/2), float3(  0.0f,  1.0f,  0.0f), float2( fXTS, fZTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2, -fZS/2), float3(  0.0f,  1.0f,  0.0f), float2( fXTS, 0.0f)),

		//left side
		CRenderer::SVert( float3( -fXS/2, -fYS/2, -fZS/2), float3( -1.0f,  0.0f,  0.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3( -fXS/2, -fYS/2,  fZS/2), float3( -1.0f,  0.0f,  0.0f), float2( 0.0f, fZTS)),
		CRenderer::SVert( float3( -fXS/2,  fYS/2,  fZS/2), float3( -1.0f,  0.0f,  0.0f), float2( fYTS, fZTS)),
		CRenderer::SVert( float3( -fXS/2,  fYS/2, -fZS/2), float3( -1.0f,  0.0f,  0.0f), float2( fYTS, 0.0f)),

		//right side
		CRenderer::SVert( float3(  fXS/2, -fYS/2, -fZS/2), float3(  1.0f,  0.0f,  0.0f), float2( 0.0f, 0.0f)),
		CRenderer::SVert( float3(  fXS/2, -fYS/2,  fZS/2), float3(  1.0f,  0.0f,  0.0f), float2( 0.0f, fZTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2,  fZS/2), float3(  1.0f,  0.0f,  0.0f), float2( fYTS, fZTS)),
		CRenderer::SVert( float3(  fXS/2,  fYS/2, -fZS/2), float3(  1.0f,  0.0f,  0.0f), float2( fYTS, 0.0f))
	};

	unsigned short iIndices[6*6] = {
		//back side
		 0, 1, 2, 0, 2, 3,
		//front side
		 6, 5, 4, 7, 6, 4,
		//bottom side
		 8, 9,10, 8,10,11,
		//top side
		12,13,14,12,14,15,
		//left side
		16,17,18,16,18,19,
		//right side
		22,21,20,23,22,20
	};	

	//create and bind VBO
	glGenBuffersARB(1, &m_uiVBOVertices);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_uiVBOVertices);
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(CRenderer::SVert)*4*6, vertBuf, GL_STATIC_DRAW_ARB);

	//create and bind IBO
	glGenBuffersARB(1, &m_uiVBOIndices);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_uiVBOIndices);
	glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(unsigned short)*6*6, iIndices, GL_STATIC_DRAW_ARB);

	m_iVerticesCount = 6*6;
	
	return 0;
}

int CModel::LoadShader(const char* szFileName)
{
	m_cgProgVertex = m_pRenderer->CompileVertexShader(szFileName);
	m_pRenderer->CGErrorNotify();
	m_cgProgFragment = m_pRenderer->CompileFragmentShader(szFileName);
	m_pRenderer->CGErrorNotify();

	return 0;
}

int CModel::DrawModel()
{
	//check if shader is loaded
	if(!m_cgProgVertex || !m_cgProgFragment)
	{
		//if not, load default
		LoadShader("data//shaders//simplepix.cg");
	}
	// Enable Pointers
	glEnableClientState( GL_VERTEX_ARRAY );				// Enable Vertex Arrays
	glEnableClientState( GL_NORMAL_ARRAY );
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );		// Enable Texture Coord Arrays

	glEnable(GL_TEXTURE_2D);
	
	if(m_bMultiTextured) //if model uses multiple textures, assign texture to sampler directly
	{
		m_pRenderer->ManageTexParams(true);		//enable user texture handling

		//send texture name and apply it to primary texture
		m_pRenderer->SetTextureParamui(m_cgProgFragment, "IN.Texture", m_pTexture->GetID());
	}
	else	//if model uses single texture, simply bind it
	{
		m_pTexture->Bind();
	}

	GLfloat WhiteSurface[] = { 1.0f, 1.0f, 1.0f, 1.0f};
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, WhiteSurface);


	glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_uiVBOVertices);
	glVertexPointer(3, GL_FLOAT, sizeof(CRenderer::SVert), (char*)NULL);
	glNormalPointer(GL_FLOAT, sizeof(CRenderer::SVert), (char*)sizeof(float3));
	glTexCoordPointer(2, GL_FLOAT, sizeof(CRenderer::SVert), (char*)(sizeof(float3)*2));
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_uiVBOIndices);

//	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//	glEnable(GL_BLEND);
	
	m_pRenderer->BindPrograms(m_cgProgVertex, m_cgProgFragment);

	m_pRenderer->SetShaderParams(m_cgProgVertex, m_cgProgFragment);

	glDrawElements(GL_TRIANGLES, GetVerticesCount(), GL_UNSIGNED_SHORT, NULL);
	//glDrawArrays(GL_TRIANGLES, 0, GetVerticesCount());

	m_pRenderer->DisableShaders();


	// Disable Pointers
	glDisableClientState( GL_VERTEX_ARRAY );				// Disable Vertex Arrays
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_TEXTURE_COORD_ARRAY );				// Disable Texture Coord Arrays

	if(m_bMultiTextured) //if model uses multiple textures, disable this functionality after drawing
	{
		m_pRenderer->ManageTexParams(false);		//disable user texture handling
	}

	glDisable(GL_TEXTURE_2D);
	return 0;
}


int CModel::LoadTexture(const char *szFileName)
{
	m_pTexture = CTextureManager::GetInstance()->GetTexture(szFileName);
	m_pTexture->CreateMipmaps();
	return 0;
}

void CModel::SetShaderParamd3(const char *szParamName, double3 d3Param)
{
	m_pRenderer->SetShaderParamd3(m_cgProgVertex, szParamName, d3Param);
	m_pRenderer->SetShaderParamd3(m_cgProgFragment, szParamName, d3Param);
}

void CModel::SetShaderParamd(const char *szParamName, double dParam)
{
	m_pRenderer->SetShaderParamd(m_cgProgVertex, szParamName, dParam);
	m_pRenderer->SetShaderParamd(m_cgProgFragment, szParamName, dParam);
}

void CModel::SetTextureParamui(const char* szParamName, unsigned int uiTexName)
{
	m_pRenderer->SetTextureParamui(m_cgProgFragment, szParamName, uiTexName);
}

